#ifndef TEST_H
#define TEST_H

#include "MiniOpenGLWidget.h"

MiniArray<Vector3f> testBasisCircle(int &M)
{
    // 参数曲面
    const Real PI = 3.1415926535;
    auto f = [=](float u, float v) -> Vector3f
    {
        return Vector3f{u, sin(u + v) * cos(u - v), v};
    };

    // 曲面上的曲线
    auto g = [=](float t) -> Vector3f
    {
        return f(2 * sin(t), 2 * cos(t));
    };

    M = 5000;
    MiniArray<Vector3f> points(M);
    for (int i = 0; i < M; i++)
        points[i] = g(2 * PI / (M - 1) * i);

    // 推入曲面上更多的点
    int um = 10;
    int vm = 8;
    MiniArray<Real> u((um + 1) * (vm + 1));
    MiniArray<Real> v((um + 1) * (vm + 1));

    // 中间洞上取的点被排除
    for (int i = 0; i < um + 1; i++)
    {
        for (int j = 0; j < vm + 1; j++)
        {
            // 记录每个点的 u,v 参数值
            int index = i * (vm + 1) + j;
            u[index] = -4 + 8.0 / um * i;
            v[index] = -4 + 8.0 / vm * j;

            Vector3f P = {u[index], v[index], 0};
            if (distance(P, {0, 0, 0}) < 2)
                continue;
            points.push_back(f(u[index], v[index]));
        }
    }
    return points;
}

MiniArray<Vector3f> testTirBoundary(int &M)
{
    const Real PI = 3.1415926;
    auto f = [=](float u, float v) -> Vector3f
    {
        Real r = 1;
        Real u1 = PI / 10 * u;
        Real u2 = PI / 10 * v;
        return Vector3f{r * cos(u1) * sin(u2),
                    r * cos(u1) * cos(u2),
                    r * sin(u1)};
    };

    Real g3 = sqrt(3);
    M = 3000;
    MiniArray<Vector3f> points(M);
    for (int i = 0; i < 1000; i++)
        points[i] = Vector3f{g3 / 1000 * i, 4, -2 + 3.0 / 1000 * i} / 4;

    for (int i = 0; i < 1000; i++)
        points[i + 1000] = Vector3f{g3 - 2 * g3 / 1000 * i, 4, 1} / 4;

    for (int i = 0; i < 1000; i++)
        points[i + 2000] = Vector3f{-g3 + g3 / 1000 * i, 4, 1 - 3.0 / 1000 * i} / 4;

    // 推入曲面上更多的点
    int um = 10;
    int vm = 8;
    MiniArray<Real> u((um + 1) * (vm + 1));
    MiniArray<Real> v((um + 1) * (vm + 1));

    // 中间洞上取的点被排除
    for (int i = 0; i < um + 1; i++)
    {
        for (int j = 0; j < vm + 1; j++)
        {
            // 记录每个点的 u,v 参数值
            int index = i * (vm + 1) + j;
            u[index] = -4 + 8.0 / um * i;
            v[index] = -4 + 8.0 / vm * j;

            Vector3f P = {u[index], v[index], 0};
            if (distance(P, {0, 0, 0}) < 2)
                continue;
            points.push_back(f(u[index], v[index]));
        }
    }
    return points;
}

MiniArray<Vector3f> testRectBoundary(int &M)
{
    const Real PI = 3.1415926;
    auto f = [=](float u, float v) -> Vector3f
    {
        return Vector3f{sin(PI / 8 * u), cos(PI / 8 * u), v / 2};
    };

    M = 4000;
    MiniArray<Vector3f> points(M);
    for (int i = 0; i < 1000; i++)
        points[i] = Vector3f{-1 + 2.0 / 1000 * i, 3, -2} / 3;

    for (int i = 0; i < 1000; i++)
        points[i + 1000] = Vector3f{1, 3, -2 + 4.0 / 1000 * i} / 3;

    for (int i = 0; i < 1000; i++)
        points[i + 2000] = Vector3f{1 - 2.0 / 1000 * i, 3, 2} / 3;

    for (int i = 0; i < 1000; i++)
        points[i + 3000] = Vector3f{-1, 3, 2 - 4.0 / 1000 * i} / 3;

    // 推入曲面上更多的点
    int um = 10;
    int vm = 8;
    MiniArray<Real> u((um + 1) * (vm + 1));
    MiniArray<Real> v((um + 1) * (vm + 1));

    // 中间洞上取的点被排除
    for (int i = 0; i < um + 1; i++)
    {
        for (int j = 0; j < vm + 1; j++)
        {
            // 记录每个点的 u,v 参数值
            int index = i * (vm + 1) + j;
            u[index] = -4 + 8.0 / um * i;
            v[index] = -4 + 8.0 / vm * j;

            Vector3f P = {u[index], v[index], 0};
            if (distance(P, {0, 0, 0}) < 2)
                continue;
            points.push_back(f(u[index], v[index]));
        }
    }

    return points;
}

MiniArray<Vector3f> testSixBoundary(int &M)
{
    M = 3000;
    MiniArray<Vector3f> points(M);

    // 建立六边形边界
    Real g3 = sqrt(3);
    Vector3f s1 = {-1, 0, -g3};
    for (int i = 0; i < 500; i++)
    {
        points[i] = s1 + Vector3f{i * 2.0 / 500, 0, 0};
    }

    Vector3f s2 = {1, 0, -g3};
    for (int i = 0; i < 500; i++)
    {
        points[i + 500] = s2 + Vector3f{i * 2.0 / 500 / 2, 0, i * g3 / 500 };
    }

    Vector3f s3 = {2, 0, 0};
    for (int i = 0; i < 500; i++)
    {
        points[i + 1000] = s3 + Vector3f{-i * 2.0 / 500 / 2, 0, i * g3 / 500};
    }

    Vector3f s4 = {1, 0, g3};
    for (int i = 0; i < 500; i++)
    {
        points[i + 1500] = s4 + Vector3f{-i * 2.0 / 500, 0, 0};
    }

    Vector3f s5 = {-1, 0, g3};
    for (int i = 0; i < 500; i++)
    {
        points[i + 2000] = s5 + Vector3f{-i * 2.0 / 500 / 2, 0, -i * g3 / 500};
    }

    Vector3f s6 = {-2, 0, 0};
    for (int i = 0; i < 500; i++)
    {
        points[i + 2500] = s6 + Vector3f{i * 2.0 / 500 / 2, 0, -i * g3 / 500};
    }

    const Real PI = 3.1415926;
    for (int i = 0; i < 25; i++)
    {
        Vector3f p = {2.5 * sin(2 * PI / 24 * i), -0.2 * g3, 2.5 * cos(2 * PI / 24 * i)};
        points.push_back(p);
    }

    for (int i = 0; i < 25; i++)
    {
        Vector3f p = {3 * sin(2 * PI / 24 * i), -0.5 * g3, 3 * cos(2 * PI / 24 * i)};
        points.push_back(p);
    }
    for (int i = 0; i < 25; i++)
    {
        Vector3f p = {3.5 * sin(2 * PI / 24 * i), -1 * g3, 3.5 * cos(2 * PI / 24 * i)};
        points.push_back(p);
    }
    for (int i = 0; i < 25; i++)
    {
        Vector3f p = {4 * sin(2 * PI / 24 * i), -2 * g3, 4 * cos(2 * PI / 24 * i)};
        points.push_back(p);
    }
    return points;
}

MiniArray<Vector3f> testCircleBoundary(int &M)
{
    // 参数曲面
    const Real PI = 3.1415926535;
    auto f = [=](float u, float v) -> Vector3f
    {
        Real r = 1;
        Real u1 = PI / 10 * u;
        Real u2 = PI / 10 * v;
        return Vector3f{r * cos(u1) * sin(u2),
                    r * cos(u1) * cos(u2),
                    r * sin(u1)};
    };

    // 曲面上的曲线
    auto g = [=](float t) -> Vector3f
    {
        return f(2 * sin(t), 2 * cos(t));
    };

    M = 5000;
    MiniArray<Vector3f> points(M);
    for (int i = 0; i < M; i++)
        points[i] = g(2 * PI / (M - 1) * i);


    // 推入曲面上更多的点
    int um = 10;
    int vm = 8;
    MiniArray<Real> u((um + 1) * (vm + 1));
    MiniArray<Real> v((um + 1) * (vm + 1));

    // 中间洞上取的点被排除
    for (int i = 0; i < um + 1; i++)
    {
        for (int j = 0; j < vm + 1; j++)
        {
            // 记录每个点的 u,v 参数值
            int index = i * (vm + 1) + j;
            u[index] = -4 + 8.0 / um * i;
            v[index] = -4 + 8.0 / vm * j;

            Vector3f P = {u[index], v[index], 0};
            if (distance(P, {0, 0, 0}) < 2)
                continue;
            points.push_back(f(u[index], v[index]));
        }
    }
    return points;
}

MiniArray<Vector3f> testFourArcBoundary(int &M)
{
    M = 4000;
    MiniArray<Vector3f> points(M);

    // 建立四个半圆边界
    const Real PI = 3.1415926;
    for (int i = 0; i < 1000; i++)
    {
        Real theta = PI / 1000 * i;
        Real a = cos(theta);
        Real b = sin(theta);
        points[i] = {a, b, -1};
    }

    for (int i = 0; i < 1000; i++)
    {
        Real theta = PI / 1000 * i;
        Real a = cos(theta);
        Real b = sin(theta);
        points[i + 1000] = {-1, b, -a};
    }

    for (int i = 0; i < 1000; i++)
    {
        Real theta = PI / 1000 * i;
        Real a = cos(theta);
        Real b = sin(theta);
        points[i + 2000] = {-a, b, 1};
    }

    for (int i = 0; i < 1000; i++)
    {
        Real theta = PI / 1000 * i;
        Real a = cos(theta);
        Real b = sin(theta);
        points[i + 3000] = {1, b, a};
    }

    // 周围点
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            Real theta = PI / 4 * j;
            Real a = cos(theta);
            Real b = sin(theta);
            points.push_back({a, b, -1 - 1.0 / 4 * i});
        }
    }

    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            Real theta = PI / 4 * j;
            Real a = cos(theta);
            Real b = sin(theta);
            points.push_back({-1 - 1.0 / 4 * i, b, -a});
        }
    }

    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            Real theta = PI / 4 * j;
            Real a = cos(theta);
            Real b = sin(theta);
            points.push_back({-a, b, 1 + 1.0 / 4 * i});
        }
    }

    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            Real theta = PI / 4 * j;
            Real a = cos(theta);
            Real b = sin(theta);
            points.push_back({1 + 1.0 / 4 * i, b, a});
        }
    }
    return points;
}

MiniArray<Vector3f> testSixArcBoundary(int &M)
{
    M = 6000;
    MiniArray<Vector3f> points(M);

    const Real PI = 3.1415926;

    // 下
    for (int i = 0; i < 1000; i++)
    {
        Real theta = PI / 2000 * i;
        Real x = cos(theta);
        Real y = sin(theta);
        points[i] = Vector3f{1 - y, -1, 1 - x};
    }

    // 右下
    for (int i = 0; i < 1000; i++)
    {
        Real theta = PI / 2000 * i;
        Real x = cos(theta);
        Real y = sin(theta);
        points[i + 1000] = Vector3f{1, -1 + x, -1 + y};
    }

    // 右上
    for (int i = 0; i < 1000; i++)
    {
        Real theta = PI / 2000 * i;
        Real x = cos(theta);
        Real y = sin(theta);
        points[i + 2000] = Vector3f{1 - x, 1 - y, -1};
    }

    // 上
    for (int i = 0; i < 1000; i++)
    {
        Real theta = PI / 2000 * i;
        Real x = cos(theta);
        Real y = sin(theta);
        points[i + 3000] = Vector3f{-1 + x, 1, -1 + y};
    }

    // 左上
    for (int i = 0; i < 1000; i++)
    {
        Real theta = PI / 2000 * i;
        Real x = cos(theta);
        Real y = sin(theta);
        points[i + 4000] = Vector3f{-1, 1 - y, 1 - x};
    }

    // 左下
    for (int i = 0; i < 1000; i++)
    {
        Real theta = PI / 2000 * i;
        Real x = cos(theta);
        Real y = sin(theta);
        points[i + 5000] = Vector3f{-1 + x, -1 + y, 1};
    }

    // 下
    for (int i = 0; i < 5; i++)
    {
        Real theta = PI / 2 / 4 * i;
        for (int j = 0; j < 5; j++)
        {
            Real x = cos(theta);
            Real y = sin(theta);
            points.push_back({1 - y, -1 - 1.0 / 4 * j, 1 - x});
        }
    }

    // 右下
    for (int i = 0; i < 5; i++)
    {
        Real theta = PI / 2 / 4 * i;
        for (int j = 0; j < 5; j++)
        {
            Real x = cos(theta);
            Real y = sin(theta);
            points.push_back({1 + 1.0 / 4 * j, -1 + x, -1 + y});
        }
    }

    // 右上
    for (int i = 0; i < 5; i++)
    {
        Real theta = PI / 2 / 4 * i;
        for (int j = 0; j < 5; j++)
        {
            Real x = cos(theta);
            Real y = sin(theta);
            points.push_back({1 - x, 1 - y, -1 - 1.0 / 4 * j});
        }
    }

    // 上
    for (int i = 0; i < 5; i++)
    {
        Real theta = PI / 2 / 4 * i;
        for (int j = 0; j < 5; j++)
        {
            Real x = cos(theta);
            Real y = sin(theta);
            points.push_back({-1 + x, 1 + 1.0 / 4 * j, -1 + y});
        }
    }

    // 左上
    for (int i = 0; i < 5; i++)
    {
        Real theta = PI / 2 / 4 * i;
        for (int j = 0; j < 5; j++)
        {
            Real x = cos(theta);
            Real y = sin(theta);
            points.push_back({-1 - 1.0 / 4 * j, 1 - y, 1 - x});
        }
    }

    // 左下
    for (int i = 0; i < 5; i++)
    {
        Real theta = PI / 2 / 4 * i;
        for (int j = 0; j < 5; j++)
        {
            Real x = cos(theta);
            Real y = sin(theta);
            points.push_back({-1 + x, -1 + y, 1 + 1.0 / 4 * j});
        }
    }
    return points;
}

MiniArray<Vector3f> testSemiBoundary(int &M)
{
    M = 4000;
    MiniArray<Vector3f> points(M);

    const Real PI = 3.1415926;
    for (int i = 0; i < 1000; i++)
        points[i] = Vector3f{2 - 4.0 / 1000 * i, 0, -1};

    for (int i = 0; i < 1000; i++)
    {
        Real theta = PI / 1000 * i;
        Real x = cos(theta);
        Real y = sin(theta);
        points[i + 1000] = Vector3f{-2, -y, x};
    }

    for (int i = 0; i < 1000; i++)
        points[i + 2000] = Vector3f{-2 + 4.0 / 1000 * i, 0, 1};

    for (int i = 0; i < 1000; i++)
    {
        Real theta = PI / 1000 * i;
        Real x = cos(theta);
        Real y = sin(theta);
        points[i + 3000] = Vector3f{2, -y, x};
    }

    for (int i = 0; i < 6; i++)
    {
        Real z = 2 - 4.0 / 5 * i;
        for (int j = 0; j < 6; j++)
        {
            points.push_back({z, 0.5 * j + 0.5, 0.5 * j + 1.5});
        }
    }

    for (int i = 0; i < 6; i++)
    {
        Real z = 2 - 4.0 / 5 * i;
        for (int j = 0; j < 6; j++)
        {
            points.push_back({z, 0.5 * j + 0.5, -0.5 * j - 1.5});
        }
    }

    for (int i = 0; i < 6; i++)
    {
        Real z = -1 + 2.0 / 5 * i;
        for (int j = 0; j < 6; j++)
        {
            points.push_back({3 + 0.5 * j, -1, z});
        }
    }

    for (int i = 0; i < 6; i++)
    {
        Real z = -1 + 2.0 / 5 * i;
        for (int j = 0; j < 6; j++)
        {
            points.push_back({-3 - 0.5 * j, -1, z});
        }
    }
    return points;
}

#endif // TEST_H
